package com.forestar.mapControl.utils.tile;

import android.annotation.SuppressLint;
import android.graphics.Point;

import com.forestar.mapControl.utils.mapConvert.geometry.coordinatesystem.CoordinateSystem;

import static com.forestar.mapControl.utils.tile.constants.TileSourceConstants.DEFAULT_TILE_SIZE;

@SuppressLint("DefaultLocale")
public class TileSchema
  {
    protected double originX;

    protected double originY;

    /**
     * 某地图级别下屏幕一像素代表的实际单位大小
     */
    protected double[] resolutions;
    protected int minLevel;
    protected int maxLevel;
    protected double width;
    protected double height;
    protected double minLevelResolution;

    protected CoordinateSystem coordSystem;
    private volatile static TileSchema defaultMercatorTileSchema = null;
    private volatile static TileSchema defaultWGS84TileSchema = null;

    private volatile static TileSchema defaultCGCS2000TileSchema = null;

    // 象素密度高的情况下，没必要取最清晰的，可以向高取一个级别
    protected int LEVEL_OFFSET_BY_DENSITY = -1;

    protected double xMin, xMax, yMin, yMax = 0;

    private Boolean isYAxisToNorth = false;

    //TODO:zq--单张瓦片的大小在一个Schema中应该是固定的值，不用后面再获取，这里有一个临时的值，后面可以再增加设置，或是初始化。
    protected int tileSize = DEFAULT_TILE_SIZE;

    /**
     * 构造瓦片源方案，各级分辨率如果2的指数关系，可以只初始化顶级分辨率即可
     */
    public TileSchema(CoordinateSystem coordSystem, double originX, double originY, int minLevel, int maxLevel, double minLevelResolution, double width, double height)
      {
        this.originX = originX;
        this.originY = originY;
        this.minLevel = minLevel;
        this.maxLevel = maxLevel;
        this.width = width;
        this.height = height;
        this.coordSystem = coordSystem;
        this.minLevelResolution = minLevelResolution;
        resolutions = new double[maxLevel + 1];
        resolutions[minLevel] = minLevelResolution;
        for (int i = minLevel + 1; i <= maxLevel; i++)
          {
            resolutions[i] = resolutions[i - 1] / 2.0;
          }
      }

    /**
     * 构造瓦片源方案，各级分辨率如果2的指数关系，可以只初始化顶级分辨率即可
     */
    public TileSchema(CoordinateSystem coordSystem, double originX, double originY, int minLevel, int maxLevel, double minLevelResolution, double width, double height, Boolean sourcePointIsEarthCenter)
      {
        this.originX = originX;
        this.originY = originY;
        this.minLevel = minLevel;
        this.maxLevel = maxLevel;
        this.width = width;
        this.height = height;
        this.coordSystem = coordSystem;
        this.minLevelResolution = minLevelResolution;
        this.isYAxisToNorth = sourcePointIsEarthCenter;
        resolutions = new double[maxLevel + 1];
        resolutions[minLevel] = minLevelResolution;
        for (int i = minLevel + 1; i <= maxLevel; i++)
          {
            resolutions[i] = resolutions[i - 1] / 2.0;
          }
      }

    /**
     * 构造瓦片方案，各级分辨率如果不是2的指数关系，或有缺级的情况，要使用此构造，传入所有级分辨率，缺级时分辨率为0
     */
    protected TileSchema(CoordinateSystem coordSystem, double originX, double originY, int minLevel, int maxLevel, double[] levelResolutions, double minLevelResolution, double width, double height)
      {
        this.originX = originX;
        this.originY = originY;
        this.minLevel = minLevel;
        this.maxLevel = maxLevel;
        this.width = width;
        this.height = height;
        this.coordSystem = coordSystem;
        resolutions = levelResolutions;
        this.minLevelResolution = minLevelResolution;
      }

    /**
     * 构造瓦片方案，各级分辨率如果不是2的指数关系，或有缺级的情况，要使用此构造，传入所有级分辨率，缺级时分辨率为0
     */
    protected TileSchema(CoordinateSystem coordSystem, double originX, double originY, int minLevel, int maxLevel, double[] levelResolutions, double width, double height)
      {
        this.originX = originX;
        this.originY = originY;
        this.minLevel = minLevel;
        this.maxLevel = maxLevel;
        this.width = width;
        this.height = height;
        this.coordSystem = coordSystem;
        resolutions = levelResolutions;
      }

    /**
     * 创建通用的墨卡托坐标系的瓦片方案
     */
    public static TileSchema createDefaultMercatorTileSchema()
      {
        if (defaultMercatorTileSchema == null)
          {
            synchronized (TileSchema.class)
              {
                if (defaultMercatorTileSchema == null)
                  {
                    defaultMercatorTileSchema = new TileSchema(CoordinateSystem.createMercator(), TileConstants.ORIGIN_X_IN_MERCATOR, TileConstants.ORIGIN_Y_IN_MERCATOR, TileConstants.MIN_LEVEL_IN_MERCATOR, TileConstants.MAX_LEVEL_IN_MERCATOR, TileConstants.MIN_LEVEL_RESOLUTION_IN_MERCATOR, TileConstants.MERCATOR_WORLD_WIDTH, TileConstants.MERCATOR_WORLD_HEIGHT);
                  }
              }
          }
        return defaultMercatorTileSchema;
      }

    /**
     * 创建通用的WGS84(参考天地图，无0级，左上角为00.)坐标系的瓦片方案
     */
    public static TileSchema createDefaultWGS84TileSchema()
      {
        if (defaultWGS84TileSchema == null)
          {
            synchronized (TileSchema.class)
              {
                if (defaultWGS84TileSchema == null)
                  {
                    defaultWGS84TileSchema = new TileSchema(CoordinateSystem.createWGS84(), TileConstants.ORIGIN_X_IN_NET_TILE, TileConstants.ORIGIN_Y_IN_NET_TILE, TileConstants.MIN_LEVEL_IN_NET_TILE, TileConstants.MAX_LEVEL_IN_NET_TILE, TileConstants.MIN_LEVEL_RESOLUTION_IN_NET_TILE, TileConstants.NET_TILE_WORLD_WIDTH, TileConstants.NET_TILE_WORLD_HEIGHT);
                  }
              }
          }
        return defaultWGS84TileSchema;
      }

    /**
     * 创建通用的WGS84(参考天地图，无0级，左上角为00.)坐标系的瓦片方案
     */
    public static TileSchema createDefaultCGCS2000TileSchema()
      {
        if (defaultCGCS2000TileSchema == null)
          {
            synchronized (TileSchema.class)
              {
                if (defaultCGCS2000TileSchema == null)
                  {
                    defaultCGCS2000TileSchema = new TileSchema(CoordinateSystem.createCGCS2000(), TileConstants.ORIGIN_X_IN_NET_TILE, TileConstants.ORIGIN_Y_IN_NET_TILE, TileConstants.MIN_LEVEL_IN_NET_TILE, TileConstants.MAX_LEVEL_IN_NET_TILE, TileConstants.MIN_LEVEL_RESOLUTION_IN_NET_TILE, TileConstants.NET_TILE_WORLD_WIDTH, TileConstants.NET_TILE_WORLD_HEIGHT);
                  }
              }
          }
        return defaultCGCS2000TileSchema;
      }

    public int getTileSize()
      {
        return tileSize;
      }

    public int getMinLevel()
      {
        return minLevel;
      }

    public int getMaxLevel()
      {
        return maxLevel;
      }

    public double getOriginX()
      {
        return originX;
      }

    public double getOriginY()
      {
        return originY;
      }

    public double getWidth()
      {
        return width;
      }

    public double getHeight()
      {
        return height;
      }

    public double getMinLevelResolution()
      {
        return minLevelResolution;
      }

    private int getLevelOffsetByPPI(float screenPPI)
      {
        // 以192为标准PPI来显示瓦片
        float STAND_SCREEN_PPI = 192;
        float PPIDiff = screenPPI - STAND_SCREEN_PPI;
        return (int) (PPIDiff / STAND_SCREEN_PPI);
      }

    public int getLevelByResolution(double resolution)
      {
        int appropriateLevel = minLevel;
        // 超越顶级时
        if (resolution > resolutions[minLevel])
          {
            return minLevel;
          }
        // 超级底级时
        if (resolution < resolutions[maxLevel])
          {
            return maxLevel;
          }
        for (int rLevel = minLevel + 1; rLevel < resolutions.length; rLevel++)
          {
            // 取出2级对比
            double resolution1 = resolutions[rLevel - 1];
            double resolution2 = resolutions[rLevel];
            // 落在两级之间
            if (resolution >= resolution2 && resolution <= resolution1)
              {
                double diff1 = resolution1 - resolution;
                double diff2 = resolution - resolution2;
                // 取出接近的
                appropriateLevel = diff1 < diff2 ? rLevel - 1 : rLevel;
              }
          }
        return appropriateLevel;
      }

    /**
     * 指定一个任意的分辨率值，获取最适合显示的级别，在两级之间时，取level值大的级别
     */
    public int getBestLevelByResolution(double resolution, float screenPPI)
      {
        int appropriateLevel = minLevel;
        // 超越顶级时
        if (resolution > resolutions[minLevel])
          {
            //在小于最小级别时，返回-1，为的是不让地图加载过多的瓦片
            return -1;
          }
        // 超级底级时
        if (resolution < resolutions[maxLevel])
          {
            return maxLevel;
          }
        for (int rLevel = minLevel + 1; rLevel < resolutions.length; rLevel++)
          {
            // 取出2级对比
            double resolution1 = resolutions[rLevel - 1];
            double resolution2 = resolutions[rLevel];
            // 落在两级之间
            if (resolution >= resolution2 && resolution <= resolution1)
              {
                double diff1 = resolution1 - resolution;
                double diff2 = resolution - resolution2;
                // 取出接近的
                appropriateLevel = diff1 < diff2 ? rLevel - 1 : rLevel;
              }
          }
        return appropriateLevel - getLevelOffsetByPPI(screenPPI);
      }

    /**
     * 计算指定瓦片级别和空间范围后，命中的瓦片序列范围
     */
    public TilesIndexBound getTilesIndexBound(int level, double minX, double maxX, double minY, double maxY)
      {
        double widthPerTile = getWidthPerTile(level, tileSize);
        if (xMin == 0 && xMax == 0 && yMin == 0 && yMax == 0)
          {
            if (isYAxisToNorth)
              {
                xMin = originX - (width / 2);
                xMax = originX + (width / 2);
                yMin = originY - (height / 2);
                yMax = originY + (height / 2);
              } else
              {
                xMin = originX;
                xMax = originX + width;
                yMin = originY - height;
                yMax = originY;
              }
          }
        //        else if (originXOffset != 0 || originYOffset != 0)
        //          {
        //            minX = Math.max(minX - originXOffset, originX);
        //            maxX = Math.min(maxX - originXOffset, originX + width);
        //            minY = Math.max(minY - originYOffset, originY - height);
        //            maxY = Math.min(maxY - originYOffset, originY);
        //          }
        boolean bIntersect = !(minX > xMax || maxX < xMin || minY > yMax || maxY < yMin);
        if (!bIntersect)
          {
            return new TilesIndexBound(level, 0, 0, 0, 0);
          } else
          {
            if (originXOffset != 0 || originYOffset != 0)
              {
                minX = minX - originXOffset;
                maxX = maxX - originXOffset;
                minY = minY - originYOffset;
                maxY = maxY - originYOffset;
              }
            minX = minX > xMin ? minX : xMin;
            maxX = maxX < xMax ? maxX : xMax;
            minY = minY > yMin ? minY : yMin;
            maxY = maxY < yMax ? maxY : yMax;
          }
        int minTileX = getColumnNumOfTile(minX, widthPerTile);
        int maxTileX = getColumnNumOfTile(maxX, widthPerTile);
        int minTileY = 0;
        int maxTileY = 0;
        maxTileY = getRowNumOfTile(maxY, widthPerTile);
        minTileY = getRowNumOfTile(minY, widthPerTile);
        if (isYAxisToNorth)
          {
            return new TilesIndexBound(level, minTileX, maxTileX, minTileY, maxTileY);
          } else
          {
            return new TilesIndexBound(level, minTileX, maxTileX, maxTileY, minTileY);
          }
      }

    /**
     * 计算指定瓦片的地理范围
     */
    public TileCoord getTileCoord(int level, int x, int y)
      {
        double unitPerTile = getWidthPerTile(level, tileSize);
        double minX = getXOfTile(x, unitPerTile);
        double maxX = minX + unitPerTile;
        double maxY;
        double minY;
        if (isYAxisToNorth)
          {
            minY = getYOfTile(y, unitPerTile);
            maxY = minY + unitPerTile;
          } else
          {
            maxY = getYOfTile(y, unitPerTile);
            minY = maxY - unitPerTile;
          }
        return new TileCoord(minX, maxX, minY, maxY);
      }

    /**
     * 获取瓦片的行数
     */
    private int getColumnNumOfTile(double x, double unitPerTile)
      {
        return (int) Math.floor((Math.abs(x - originX)) / unitPerTile);
      }

    /**
     * 获取瓦片的列数
     */
    private int getRowNumOfTile(double y, double unitPerTile)
      {
        if (isYAxisToNorth)
          {
            return (int) Math.floor((Math.abs(y - originY)) / unitPerTile);
          } else
          {
            return (int) Math.floor((Math.abs(originY - y)) / unitPerTile);
          }
      }

    public Point getTileIndex(double x, double y, int level)
      {
        double widthPerTile = getWidthPerTile(level, tileSize);
        int tileX = getColumnNumOfTile(x, widthPerTile);
        int tileY = getRowNumOfTile(y, widthPerTile);
        return new Point(tileX, tileY);
      }

    /**
     * 获取瓦片的边长
     */
    public double getWidthPerTile(int level, int tileSize)
      {
        if (level > maxLevel)
          {
            level = maxLevel;
          } else if (level < minLevel)
          {
            level = minLevel;
          }
        return resolutions[level] * tileSize;
      }

    public double getResolutionByLevel(int level)
      {
        double resolution = -1;
        if (level <= maxLevel && level >= minLevel)
          {
            resolution = resolutions[level];
          }
        return resolution;
      }

    /**
     * 获取瓦片的x坐标
     */
    public double getXOfTile(int x, double unitPerTile)
      {
        return originX + originXOffset + x * unitPerTile;
      }

    /**
     * 获取瓦片的y坐标
     */
    public double getYOfTile(int y, double unitPerTile)
      {
        if (isYAxisToNorth)
          {
            return y * unitPerTile - originY + originYOffset;
          } else
          {
            return originY + originYOffset - y * unitPerTile;
          }
      }

    public CoordinateSystem getCoordinateSystem()
      {
        return this.coordSystem;
      }

    public double getxMin()
      {
        return xMin;
      }

    public void setxMin(double xMin)
      {
        this.xMin = xMin;
      }

    public double getxMax()
      {
        return xMax;
      }

    public void setxMax(double xMax)
      {
        this.xMax = xMax;
      }

    public double getyMin()
      {
        return yMin;
      }

    public void setyMin(double yMin)
      {
        this.yMin = yMin;
      }

    public double getyMax()
      {
        return yMax;
      }

    public void setyMax(double yMax)
      {
        this.yMax = yMax;
      }

    public void setOriginX(double originX)
      {
        this.originX = originX;
      }

    public void setOriginY(double originY)
      {
        this.originY = originY;
      }

    protected double originXOffset = 0.0;
    protected double originYOffset = 0.0;

    public double getOriginXOffset()
      {
        return originXOffset;
      }

    public void setOriginXOffset(double originX1)
      {
        originXOffset = originX1;
        // originX = originX + originX1;
      }

    public void setOriginXOffsetCumsum(double originX1)
      {
        originXOffset = originXOffset + originX1;
        // originX = originX + originX1;
      }

    public double getOriginYOffset()
      {
        return originYOffset;
      }

    public void setOriginYOffset(double originY1)
      {
        originYOffset = originY1;
        // originY = originY + originY1;
      }

    public void setOriginYOffsetCumsum(double originY1)
      {
        originYOffset = originYOffset + originY1;
        // originX = originX + originX1;
      }

    public boolean isYAxisToNorth()
      {
        return isYAxisToNorth;
      }
  }
